package com.heima.article.service.impl;

import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.heima.article.mapper.ApArticleMapper;
import com.heima.article.service.HotArticleService;
import com.heima.common.constants.ArticleConstants;
import com.heima.common.constants.RedisConstants;
import com.heima.common.dtos.ResponseResult;
import com.heima.model.article.dtos.ArticleVisitStreamMsg;
import com.heima.model.article.pojos.ApArticle;
import com.heima.model.wemedia.dtos.HotArticleVo;
import com.heima.model.wemedia.pojos.WmChannel;
import com.heima.utils.common.BeanHelper;
import com.heima.utils.common.JsonUtils;
import com.heima.wemedia.feign.WemediaFeign;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class HotArticleServiceImpl implements HotArticleService {
    @Autowired
    private ApArticleMapper apArticleMapper;

    @Autowired
    private WemediaFeign wemediaFeign;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Override
    public void computeHotArticle() {
        //查询前5天的所有App文章
        //查询前第5天的日期
        Date days = DateTime.now().minusDays(5).toDate();
        List<ApArticle> apArticleList = apArticleMapper.findArticleListByLastDays(days);

        //统计所有文章的分值
        List<HotArticleVo> hotArticleVoList = computeArticleScore(apArticleList);

        //按照文章频道过滤文章
        ResponseResult<List<WmChannel>> responseResult = wemediaFeign.channels();
        if (responseResult.getCode().equals(200)) {
            List<WmChannel> channelList = responseResult.getData();
            if (CollectionUtils.isNotEmpty(channelList)) {
                for (WmChannel channel : channelList) {
                    //取每个频道分值较高的前30条
                    //方式一：普通for循环
                    /*List<HotArticleVo> curChanelArticles = new ArrayList<>();
                    for(HotArticleVo vo:hotArticleVoList){
                        if(vo.getChannelId().equals(channel.getId())){
                            curChanelArticles.add(vo);
                        }
                    }*/

                    //方式二：使用stream的filter实现
                    List<HotArticleVo> curChanelArticles = hotArticleVoList.stream()
                                                                   .filter(vo -> vo.getChannelId().equals(channel.getId()))
                                                                   .collect(Collectors.toList());
                    String key = RedisConstants.HOT_ARTICLE_FIRST_PAGE + channel.getId();
                    //存入Redis缓存
                    sortAndCacheToRedis(key, curChanelArticles);
                }
            }
        }

        //单独缓存推荐频道数据
        String key = RedisConstants.HOT_ARTICLE_FIRST_PAGE + RedisConstants.DEFAULT_TAG;
        sortAndCacheToRedis(key, hotArticleVoList);
    }

    /**
     * 对文章按照score倒序，如果超过30条截取前30条，缓存到Redis
     *
     * @param key
     * @param articleList
     */
    private void sortAndCacheToRedis(String key, List<HotArticleVo> articleList) {
        //对文章按照score倒序
        articleList.sort((HotArticleVo o1, HotArticleVo o2) ->  o2.getScore() - o1.getScore());

        //如果超过30条截取前30条
        if (articleList.size() > 30) {
            articleList = articleList.subList(0, 30);
        }

        //缓存到Redis
        redisTemplate.opsForValue().set(key, JsonUtils.toString(articleList));
    }

    private List<HotArticleVo> computeArticleScore(List<ApArticle> apArticleList) {
        List<HotArticleVo> voList = new ArrayList<>();
        for (ApArticle apArticle : apArticleList) {
            HotArticleVo vo = BeanHelper.copyProperties(apArticle, HotArticleVo.class);
            //计算文章分值
            int score = computeScore(apArticle);
            vo.setScore(score);
            voList.add(vo);
        }
        return voList;
    }

    private int computeScore(ApArticle apArticle) {
        int score = 0;
        if (apArticle.getViews() != null) {
            score += apArticle.getViews();
        }
        if (apArticle.getLikes() != null) {
            score += apArticle.getLikes() * ArticleConstants.HOT_ARTICLE_LIKE_WEIGHT;
        }
        if (apArticle.getComment() != null) {
            score += apArticle.getComment() * ArticleConstants.HOT_ARTICLE_COMMENT_WEIGHT;
        }
        if (apArticle.getCollection() != null) {
            score += apArticle.getCollection() * ArticleConstants.HOT_ARTICLE_COLLECTION_WEIGHT;
        }

        return score;
    }

    @Override
    public void updateArticle(ArticleVisitStreamMsg streamMsg) {
        Long articleId = streamMsg.getArticleId();
        ApArticle apArticle = apArticleMapper.selectById(articleId);
        if (apArticle == null) {
            return;
        }

        Long view = streamMsg.getView();
        Long like = streamMsg.getLike();
        Long comment = streamMsg.getComment();
        Long collect = streamMsg.getCollect();

        if (view != null && apArticle.getViews() != null) {
            apArticle.setViews(apArticle.getViews() + view.intValue());
        }
        if (like != null && apArticle.getLikes() != null) {
            apArticle.setLikes(apArticle.getLikes() + like.intValue());
        }
        if (collect != null && apArticle.getCollection() != null) {
            apArticle.setCollection(apArticle.getCollection() + collect.intValue());
        }
        if (comment != null && apArticle.getComment() != null) {
            apArticle.setComment(apArticle.getComment() + comment.intValue());
        }
        apArticleMapper.updateById(apArticle);

        //更新Redis缓存数据
        String key = RedisConstants.HOT_ARTICLE_FIRST_PAGE + apArticle.getChannelId();
        //重新计算最新的分值
        int newScore = computeScore(apArticle);
        HotArticleVo hotArticleVo = BeanHelper.copyProperties(apArticle, HotArticleVo.class);
        hotArticleVo.setScore(newScore);

        updateCache(key, hotArticleVo);
    }

    private void updateCache(String key, HotArticleVo hotArticleVo) {
        /**
         * 情况一：新分值的文章 如果已经存在缓存中，直接替换缓存数据，重新排序，重新刷新缓存
         * 情况二：新分值的文章 如果不存在缓存中，把新文章直接存入缓存列表，重新排序，截取数据，重新刷新缓存
         */
        String redisData = redisTemplate.opsForValue().get(key);
        if (StringUtils.isNotEmpty(redisData)) {
            List<HotArticleVo> hotArticleVoList = JsonUtils.toList(redisData, HotArticleVo.class);

            //判断新分值的文章是否在缓存中
            boolean flag = false;
            for (HotArticleVo vo : hotArticleVoList) {
                //存在
                if (hotArticleVo.getId().equals(vo.getId())) {
                    //替换新分值
                    vo.setScore(hotArticleVo.getScore());
                    flag=true;
                }
            }

            //判断新文章不在缓存中
            if(!flag){
                //把新文章直接存入缓存列表
                hotArticleVoList.add(hotArticleVo);
            }

            //重新排序，截取数据，刷新缓存
            sortAndCacheToRedis(key,hotArticleVoList);
        }
    }
}


